package com.spzx.common.security.aspect;

import com.spzx.common.core.utils.uuid.UUID;
import com.spzx.common.redis.util.DistributedLockUtil;
import com.spzx.common.security.annotation.SpzxCache;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
public class SpzxCacheAspect {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(spzxCache)")
    public Object cache(ProceedingJoinPoint joinPoint, SpzxCache spzxCache) {
        //1、查询缓存是否存在
        //缓存的key
        String cacheKey = spzxCache.cachePrefix();
        //分布式锁的唯一标识
        String token = UUID.fastUUID().toString();

        Object[] args = joinPoint.getArgs();
        if (!ArrayUtils.isEmpty(args)) {
            cacheKey += ":" + ArrayUtils.toString(args);
        }

        try {
            //2、如果存在则直接返回
            Object cacheResult = redisTemplate.opsForValue().get(cacheKey);
            if (cacheResult != null) {
                return cacheResult;
            }
            //3、如果不存在则执行方法，并将结果存入缓存
            // 3.1 加分布式锁
            DistributedLockUtil.builder(stringRedisTemplate).getLock(spzxCache.lockPrefix(), token, spzxCache.lockTimeout());
            // 3.2 查询缓存是否存在，如果存在则直接返回
            cacheResult = redisTemplate.opsForValue().get(cacheKey);
            if (cacheResult != null) {
                return cacheResult;
            }
            // 3.3 执行方法，并将结果存入缓存
            Object result = joinPoint.proceed();
            if (result == null){
                redisTemplate.opsForValue().set(cacheKey, result, spzxCache.emptyCacheTimeout(), TimeUnit.SECONDS);
            }else {
                redisTemplate.opsForValue().set(cacheKey, result, spzxCache.cacheTimeout()+new Random()
                        .nextInt(spzxCache.randomFactor()), TimeUnit.SECONDS);
            }
            // 3.4 返回结果
            return result;
        } catch (Throwable e) {
            try {
                return joinPoint.proceed();
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            // 3.4 释放分布式锁
            DistributedLockUtil.builder(stringRedisTemplate).unLock(spzxCache.lockPrefix(), token);
        }
    }
}
